/**
 * TranslationFactory.java
 *
 * Copyright (C) 2009,  Tomislav Milkovic
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
package extjsdyntran.translation;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;


import org.apache.commons.fileupload.FileItem;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

/**
 * @description Main class for translation services.
 * It is used to register translation services. When translation is in progress,
 * if there is no supported language for current service, next service will be used for translation   
 */
public class TranslationFactory  {

	private static ArrayList<TranslationIntf> services = new ArrayList<TranslationIntf>();
	
	//on initialization add service to factory 
	public static void appendService(String service, String username, String pasword, String path){
		
		try{
			TranslationIntf transervice = initService( service,username,pasword, path);
			services.add(transervice);
			transervice.loadLanguages();

		}catch (Exception e){
			new Exception(e);			
		}	
	}
	
	
	//init translation service class; 
	private static TranslationIntf initService(String service, String username , String password, String path) throws Exception{
		if (service==null) return null;
		
			Class<?> parTypes[] = new Class[3];
			parTypes[0] = java.lang.String.class;
			parTypes[1] = java.lang.String.class;
			parTypes[2] = java.lang.String.class;
		
			Class<?> cl = Class.forName(service);
			Constructor<?> ct = cl.getDeclaredConstructor(parTypes);
			ct.setAccessible(true);
			
			Object[] argList  = new Object[3];
	        argList[0] = username;
	        argList[1] = password;
	        argList[2] = path;
	        
	        Object retObj = ct.newInstance(argList);
	        			
			return (TranslationIntf)retObj;
	}
	
	//get first service supporting language for translation
	public static TranslationIntf getService(String language){
		Iterator<TranslationIntf> it =  services.iterator();
		TranslationIntf service=null;
		while (it.hasNext()){
			service = it.next();
			if (service.isLanguageSupported(language)) {return service;};
		}
		return service;
	}

	//get first service supporting language editing (add,remove languages)
	public static TranslationIntf getEditableService(String language){
		Iterator<TranslationIntf> it =  services.iterator();
		TranslationIntf service=null;
		while (it.hasNext()){
			service = it.next();
			if (!service.isNewLanguageSupported()) continue;
			return service;
		}
		return service;
	}
	
	//save specific language or all languages on all services 
	public static boolean saveLanguages(String language){
		Iterator<TranslationIntf> it =  services.iterator();
		TranslationIntf service=null;
		while (it.hasNext()){
			service = it.next();
			if ("*".equals(language))  {
				service.saveLanguages(language);
			}else if (service.isLanguageSupported(language)) { 
				return service.saveLanguages(language);
			};
		}
		return true;
	}
	
	//return number of registered services, if =0 then servlet should stop processing
	public static int size(){
		return services.size();
	}
	

	
	
	//sort and create JSON response of language list
	private static JSONArray sortLanguages(Properties lg){
		Enumeration <Object> keys = lg.keys();
		List <String> elementList = new ArrayList<String>();
		while (keys.hasMoreElements()) {
			elementList.add((String)keys.nextElement());
		}

		Collections.sort(elementList);
		
		JSONArray result = new JSONArray();
		Iterator<String> it = elementList.iterator();
		while (it.hasNext()){
			String key = it.next();
			JSONArray lang = new JSONArray();
			lang.put(lg.get(key));
			lang.put(key);
			result.put(lang);
		}

		return result;
	}
	
	//retrieve all languages from all registered services
	public static String getLanguages(){
		Properties langs = new Properties();
		Iterator<TranslationIntf> it =  services.iterator();
		TranslationIntf service=null;
		while (it.hasNext()){
			service = it.next();
			Properties prop = service.getLanguages();
			langs.putAll(prop);
		}	
		JSONArray res = sortLanguages(langs);
		return res.toString();
		
	}
	
	//reload languages in all registered services
	public static void reloadLanguages(){
		Iterator<TranslationIntf> it =  services.iterator();
		TranslationIntf service=null;
		while (it.hasNext()){
			service = it.next();
		}		
	}
	
	//does a test of every translation service and returns list of failed services
	public static boolean test(JSONObject result){
		Iterator<TranslationIntf> it =  services.iterator();
		TranslationIntf service=null;
		JSONArray data = new JSONArray(); 
		while (it.hasNext()){
			service = it.next();
			JSONObject obj =  new JSONObject();
			try {
				obj.put("service" , service.getClass().getCanonicalName());
				if (service.test()){					
					obj.put("status" , true);

				}else obj.put("status" , false);
			} catch (Exception e) {
				try {
					obj.put("status" , false);
				} catch (JSONException e1) {
					e1.printStackTrace();
				}
				e.printStackTrace();
				return false;
			}
			data.put(obj);
		}			
		
		try {
			result.append("log", data);
		} catch (JSONException e) {
			e.printStackTrace();
		}
		return true;
	}
	
	
	
	private static void compressSingleService(ZipOutputStream zout, TranslationIntf service, String language) throws Exception{

		ByteArrayOutputStream langout = service.compressCache(language);
	    zout.putNextEntry(new ZipEntry(service.getClass().getCanonicalName())); 
	    zout.write(langout.toByteArray(),0,langout.size());
	    zout.closeEntry(); 
	}
	

	public static ByteArrayOutputStream compressCache(String language) throws Exception{
		ByteArrayOutputStream bout=new ByteArrayOutputStream();
		ZipOutputStream zout=new ZipOutputStream(bout);
		
        if(language!=null){
        	TranslationIntf service = getService(language);
        	compressSingleService(zout,service,language);
        }else{
			Iterator<TranslationIntf> it =  services.iterator();
			TranslationIntf service=null;
			while (it.hasNext()){
				service = it.next();
				compressSingleService(zout,service,null);
			}	
        }
		
		zout.finish();
		return bout ; 
	}
	
	private static TranslationIntf getServiceByClassName(String className){
		
		Iterator<TranslationIntf> it =  services.iterator();
		TranslationIntf service=null;
		while (it.hasNext()){
			service = it.next();
			if(className.equals(service.getClass().getCanonicalName())) return service;
		}	
		
		return null; 
	}
	

	public static boolean deCompressCache(FileItem item) throws IOException{
			
        ZipInputStream zin = new ZipInputStream(new ByteArrayInputStream(item.get()));
        ZipEntry zEntry = null;      
        
        while( (zEntry = zin.getNextEntry()) != null) {
        	TranslationIntf service = getServiceByClassName(zEntry.getName());
            ByteArrayOutputStream bos = new ByteArrayOutputStream();           
            int size;
            byte[] buffer = new byte[1024]; 
            while ((size = zin.read(buffer, 0, buffer.length)) != -1) {
         	   bos.write(buffer, 0, size);
         	   }
            
			try {
				service.deCompressCache(bos.toByteArray());
			} catch (Exception e) {
				e.printStackTrace();
				return false;
			}	
        }
        
        return true;
	}
	
}
